home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The Programmer Disk
/
The Programmer Disk (Microforum).iso
/
xpro
/
c3
/
pro24
/
cintr.c
< prev
next >
Wrap
Text File
|
1986-08-06
|
10KB
|
318 lines
/* cintr.c -- code for interrupt management */
/*****************************************************************************
* Change Log
* Date | Change
*-----------+-----------------------------------------------------------------
* 31-Dec-85 | Created changelog
* 31-Dec-85 | Add c:\ to include directives
* 31-Dec-85 | Save actual interrupt vector data; don't use constants
* | (different on PC/AT and PC/XT)
* 31-Dec-85 | Added intr_init
* 31-Dec-85 | Handle secondary interrupt port for AT
* 26-May-86 | Use cmdline.c to get debug switch
* 6-Aug-86 | Modified for Lattice 3.0 -- use "void" to type some routines
*****************************************************************************/
/*
* I think this came from Dale Amon, but we had some problems
* getting it to work with Lattice C and hacked away to get
* something to do the job.
*/
#include "stdio.h"
#include "dos.h"
/* note: dos.h typedefs byte to unsigned char */
/* cext.h #defines byte to unsigned char */
#include "cext.h"
#include "cmdline.h"
#include "cintr.h"
#include "atxt.h"
/*
* IBM-XT and IBM-AT interrupt information
*/
#define NIRQS 16 /* Maximum number of IRQ levels */
short defpc[NIRQS]; /* restore the vector to this PC */
short defseg[NIRQS]; /* and this segment */
#define VECTOR(irq) (0x20 + 4 * (irq))
/* Maps interrupt request number
to the address of the vector */
/* CAUTION! CAUTION! CAUTION! CAUTION! CAUTION! CAUTION!
The above "vector" computation DOES NOT WORK for a PC/AT for
values higher than 7. IRQ8..15 are vectored starting at 70.
The proper AT computation, should higher IRQ values be required,
is
(0x20 + ((irq) < 8 ? 0x20 + 4*(irq) : 0x70 + 4 * ((irq) - 8)))
but that is much too complicated to want to do in the restricted
case of having only an IRQ2 device. But you have now been warned.
Note also that devices which on the PC or XT interrupt on IRQ2 will
interrupt on the AT at IRQ9, but the software in the BIOS redirects
this interrupt (PC/AT Tech Ref page 5-71) via the IRQ2 vector.
*** HOWEVER ***
If the program masks off interrupts by twiddling the mask bit for IRQ2
it will also mask off the realtime clock interrupt, coprocessor
interrupt, and *FIXED DISK INTERRUPT*. Therefore, any attempt to
manipulate the interrupt masks must be done to the secondary 8259
controller channel chip, located at locations 0xA0 and 0xA1.
Mask bits in primary mask register:
Bit XT* AT**
7 Printer Parallel Port 1
6 Diskette Diskette controller
5 Fixed Disk Parallel Port 2
4 Serial Port 1 Serial Port 1
3 Serial Port 2 Serial Port 2
2 IRQ2 IRQ2 (OR of IRQ8..IRQ15)
1 Keyboard Keyboard
0 Timer Timer
Mask bits in secondary mask register (AT only)
7 IRQ15
6 Fixed disk controller
5 Coprocessor
4 IRQ12
3 IRQ11
2 IRQ10
1 IRQ9 (Bus IRQ2)
0 Realtime clock
Source:
* PC/XT Technical Ref page 1-9
** PC/AT Technical Ref page 1-11
*/
#define INT_0_CONTROL_PORT 0x20 /* Interrupt chip I/0 port (PC, XT)*/
#define INT_1_CONTROL_PORT 0x21 /* " " " " " */
#define INT2_0_CONTROL_PORT 0xA0 /* Interrupt chip 2 I/O port (AT) */
#define INT2_1_CONTROL_PORT 0xA1 /* " " " " " */
#define EOI(irq) (0x60 | (irq))
/* End of interrupt command to
send to PORT1 */
#define ndsw 2
private char *dsw[ndsw] = { "-d", "-debug" };
int enabled; /* flag to tell if interrupts are on */
int debug_intr; /* set for debugging */
int IsAT; /* AT or XT?, initialized here, read-only to others */
/****************************************************************************
* Routines local to this module
****************************************************************************/
private void print_intr();
/****************************************************************************
* intr_init
* Effect:
* Initializes the interrupt routines
****************************************************************************/
void intr_init()
{
int i;
debug_intr = (cl_nswitch(dsw, ndsw) != NULL);
IsAT = (ATXT() == ISAT);
if (debug_intr) printf("IsAT is %d\n", IsAT);
for (i = 0; i < NIRQS; i++) defpc[i] = defseg[i] = 0;
}
/****************************************************************************
* intr_enable
* Inputs:
* int irq: Interrupt level to enable
* Effect:
* Enables the interrupt in the control port, sets enabled flag true
****************************************************************************/
void intr_enable(irq)
int irq;
{
int pv;
if (IsAT && irq == 2) { /* AT on irq2 */
pv = inp(INT2_1_CONTROL_PORT);
if (debug_intr)
printf("intr_enable/AT: (before) %02x = %02x\n",
INT2_1_CONTROL_PORT, pv);
pv &= ~(1<<1);
outp(INT2_1_CONTROL_PORT, pv);
if (debug_intr) { /* report */
pv = inp(INT2_1_CONTROL_PORT);
printf("intr_enable/AT: (after) %02x = %02x\n",
INT2_1_CONTROL_PORT, pv);
} /* report */
} /* AT on irq2 */ else { /* PC or XT or AT not IRQ2 */
pv = inp(INT_1_CONTROL_PORT);
pv &= ~(1<<irq);
outp(INT_1_CONTROL_PORT, pv);
} /* PC or XT or AT not IRQ2 */
enabled = true;
}
/****************************************************************************
* intr_disable
* Inputs:
* int irq: irq level to disable
* Effect:
* Disables the interrupt in the control registers, sets enabled false
****************************************************************************/
void intr_disable(irq)
int irq;
{
int pv;
if(IsAT && irq == 2)
{ /* AT */
/* On the AT, we mask the interrupt in the secondary register */
pv = inp(INT2_1_CONTROL_PORT);
if(debug_intr)
printf("intr_disable/AT: (before) %02x = %02x\n",INT2_1_CONTROL_PORT,pv);
pv |= (1<<1);
outp(INT2_1_CONTROL_PORT,pv);
} /* AT */
else
{ /* PC or XT */
pv = inp(INT_1_CONTROL_PORT);
pv |= (1<<irq);
outp(INT_1_CONTROL_PORT, pv);
if(debug_intr)
{ /* report */
pv = inp(INT2_1_CONTROL_PORT);
printf("intr_enable/XTAT: (after) %02x = %02x\n",INT2_1_CONTROL_PORT,pv);
} /* report */
} /* PC or XT */
enabled = 0;
}
/****************************************************************************
* intr_routine
* Inputs:
* int irq: interrupt level
* Effect:
* Set up vector and aintr (assembler code) globals so that
* the function a_intr is called when an interrupt occurs
* Notes:
* Possible bug: The segment numbers (esp. ds) may not be correct and
* probably should be passed as arguments
****************************************************************************/
int a_irq, a_intr(), a_dsreg;
void intr_routine(irq)
int irq;
{
struct SREGS s;
segread(&s);
a_irq = irq;
intr_set_vector(irq, s.cs, a_intr, 1);
a_dsreg = s.ds;
if (debug_intr) printf("cs = %x\tds = %x\n", s.cs, s.ds);
}
/****************************************************************************
* intr_cleanup
* Inputs:
* int irq: irq level to restore interrupt for
* Effect:
* Restores the interrupt vector for the irq
****************************************************************************/
void intr_cleanup(irq)
int irq;
{
if (defseg[irq] != 0)
intr_set_vector(irq, defseg[irq], defpc[irq],0);
if (debug_intr)
print_intr("After cleanup",irq);
}
/****************************************************************************
* intr_eoi
* Inputs:
* int irq: interrupt level to do this to
* Effect:
* Sends end of interrupt command to the port for the indicated
* interrupt level
*
* On the AT, the IRQ2 interrupt really came in on vector 71, but
* EOI has been sent to the secondary I/O port and the interrupt has
* been revectored to 0A. See BIOS listing for the AT page 5-71.
****************************************************************************/
void intr_eoi(irq)
int irq;
{
outp(INT_0_CONTROL_PORT, EOI(irq));
}
/****************************************************************************
* intr_set_vector
* Inputs:
* int irq: interrupt vector level to set
* short seg: segment value to store in interrupt level
* short pc: pc value to store in interrupt level
* boolean save: true to save the old value, false to just overwrite
* it
* Effect:
* Sets the interrupt vector cs:ip to the indicated values
****************************************************************************/
void intr_set_vector(irq, seg, pc, save)
int irq;
short seg;
short pc;
int save;
{
short s[2];
short o[2];
s[0] = pc;
s[1] = seg;
if (debug_intr)
print_intr("Before poking new vector",irq);
peek(0, VECTOR(irq), (char *) o, sizeof(o));
poke(0, VECTOR(irq), (char *) s, sizeof(s));
if (save) { /* save vector */
defseg[irq] = o[1];
defpc[irq] = o[0];
} /* save vector */
if (debug_intr) print_intr("After poking new vector",irq);
}
/****************************************************************************
* print_intr
* Inputs:
* char * msg: Identifying message
* int irq: Interrupt to print
* Effect:
* Prints the contents of the interrupt vector
****************************************************************************/
private void print_intr(msg,irq)
char * msg;
int irq;
{
short s[2];
peek(0, VECTOR(irq), (char *) s, sizeof(s));
printf(
"%s: vector %d, address %x, pc = %x, seg = %x, defpc=%x, defseg=%x\n",
msg, irq, VECTOR(irq), s[0], s[1],defpc[irq],defseg[irq]);
}